module Project3(SW,KEY,LEDR,LEDG,HEX0,HEX1,HEX2,HEX3,CLOCK_50);
  input  [9:0] SW;
  input  [3:0] KEY;
  input  CLOCK_50;
  output [9:0] LEDR;
  output [7:0] LEDG;
  output [6:0] HEX0,HEX1,HEX2,HEX3;

  parameter DBITS    =32;
  parameter INSTSIZE =32'd4;
  parameter INSTBITS =32;
  parameter STARTPC  =32'h20;
  parameter REGNOBITS=5;
  parameter IMMBITS=16;
  parameter ADDRKEY  =32'hFFFFFF00;
  parameter ADDRKDATA=32'hFFFFFF00;
  parameter ADDRKCTRL=32'hFFFFFF04;
  parameter ADDRSW   =32'hFFFFFF10;
  parameter ADDRSDATA=32'hFFFFFF10;
  parameter ADDRSCTRL=32'hFFFFFF14;
  parameter ADDRHEX =32'hFFFFFF80;
  parameter ADDRHBLNK=32'hFFFFFF84;
  parameter ADDRLEDR=32'hFFFFFF90;
  parameter ADDRLEDG=32'hFFFFFFA0;
  parameter IMEMINITFILE="clock.mif";
  parameter IMEMADDRBITS=13;
  parameter IMEMWORDBITS=2;
  parameter IMEMWORDS=2048;
  parameter DMEMADDRBITS=13;
  parameter DMEMWORDBITS=2;
  parameter DMEMWORDS=2048;
 
  parameter OP1_ALUR=6'o00;
  parameter OP1_ANDI=6'o01;
  parameter OP1_ORI =6'o02;
  parameter OP1_HI  =6'o03;
  parameter OP1_ADDI=6'o04;
  parameter OP1_LW  =6'o20;
  parameter OP1_SW  =6'o21;
  parameter OP1_JAL =6'o40;
  parameter OP1_BEQ =6'o41;
  parameter OP1_BNE =6'o45;
 
  parameter OP2_AND =6'o11;
  parameter OP2_OR  =6'o12;
  parameter OP2_XOR =6'o13;
  parameter OP2_NAND=6'o15;
  parameter OP2_NOR =6'o16;
  parameter OP2_NXOR=6'o17;
  parameter OP2_ADD =6'o20;
  parameter OP2_SUB =6'o21;
  parameter OP2_EQ  =6'o41;
  parameter OP2_LT  =6'o42;
  parameter OP2_LE  =6'o43;
  parameter OP2_NE  =6'o45;
  parameter OP2_GE  =6'o46;
  parameter OP2_GT  =6'o47;
  // This is not a valid opcode2 value, but it is a valid ALUfunc value
  parameter OP2_LOHI=6'o77;
  
  parameter CLOCKS_PER_MS=83300;
  parameter CLOCKS_PER_10MS=833000;

  wire clk,lock;
  Pll pll(.inclk0(CLOCK_50),.c0 (clk),.locked(lock));
  wire reset=!lock;
 
  // The PC register and update logic
	wire [11:0] iaddr_F = PC[(IMEMADDRBITS-1):IMEMWORDBITS];
	reg  [(DBITS-1):0] PC;
	reg [11:0] iaddr_A, iaddr_M;
	always @(posedge clk) begin
		if(reset)
			PC<=STARTPC;
		else if(mispred_B) begin
			PC<=pcgood_B;
			bpred_target <= pcgood_A;
			bpred_from <= iaddr_A;
		end
		else if(!stall_F)
			PC<=pcpred_F;
		iaddr_A <= iaddr_F;
		//iaddr_M <= iaddr_A;
		//bpred[iaddr_M] <= pcgood_M;
  end
  // This is the value of "incremented PC", computed in stage 1
  wire [(DBITS-1):0] pcplus_F=PC+32'd4;
  // This is the predicted value of the PC
  // that we used to fetch the next instruction
  wire [(DBITS-1):0] pcpred_F=(iaddr_F==bpred_from) ? bpred_target : pcplus_F;
  
  reg [(DBITS-1):0] bpred_target, bpred_from;

  // Instruction-fetch
  (* ram_init_file = IMEMINITFILE *)
  reg [(DBITS-1):0] imem[(IMEMWORDS-1):0];
  wire [(DBITS-1):0] inst_F=imem[PC[(IMEMADDRBITS-1):IMEMWORDBITS]];
 // If fetch and decoding stages are the same stage,
 // just connect signals from fetch to decode
 wire [(DBITS-1):0] inst_D=inst_F;
 wire [(DBITS-1):0] pc_D=PC;
 wire [(DBITS-1):0] pcplus_D=pcplus_F;
 wire [(DBITS-1):0] pcpred_D=pcpred_F;

 // Instruction decoding
 // These have zero delay from inst_D
 // because they are just new names for those signals
 wire [5:0]  op1_D=inst_D[31:26];
 wire [4:0]  rx_D =inst_D[25:21];
 wire [4:0]  ry_D =inst_D[20:16];
 wire [4:0]  rz_D =inst_D[15:11];
 wire [5:0]  op2_D=inst_D[ 5: 0];
 wire [(IMMBITS-1):0] rawimm_D=inst_D[(IMMBITS-1):0];
 // Normal and branch-shifted sign-extended immediate
 wire [(DBITS-1):0] broffs_D={{(DBITS-IMMBITS-2){rawimm_D[IMMBITS-1]}},rawimm_D,2'b00};
 // Register-read
 //(* ram_init_file = "Regs.mif" *)
 //(* ram_init_file = "Regs.mif", ramstyle="logic" *)
 (* ramstyle="logic" *)
 reg [(DBITS-1):0] regs[31:0];
 // Two read ports, always using rx and ry for register numbers
	wire [4:0] rregno1_D=rx_D, rregno2_D=ry_D;
 //wire [(DBITS-1):0] regval1_D=regs[rregno1_D];
 //wire [(DBITS-1):0] regval2_D=regs[rregno2_D];
	wire forwardr1_A = (wregno_A == rregno1_D) && wrreg_A;
	wire forwardr2_A = (wregno_A == rregno2_D) && wrreg_A;
	wire forwardr1_M = (wregno_M == rregno1_D) && wrreg_M;
	wire forwardr2_M = (wregno_M == rregno2_D) && wrreg_M;
	wire [(DBITS-1):0] regval1_D= forwardr1_A ? aluout_A :
											forwardr1_M ? wregval_M : regs[rregno1_D];
	wire [(DBITS-1):0] regval2_D= forwardr2_A ? aluout_A :
											forwardr2_M ? wregval_M : regs[rregno2_D];
 
 wire stall_A=1'b0;

 wire dep1_A2D=wrreg_A&&selmemout_A&&(wregno_A==rregno1_D);
 wire dep2_A2D=wrreg_A&&selmemout_A&&(wregno_A==rregno2_D);
 wire dep1_M2D=wrreg_M&&selmemout_M&&(wregno_M==rregno1_D);
 wire dep2_M2D=wrreg_M&&selmemout_M&&(wregno_M==rregno2_D);

 wire stall_D=dep1_A2D||dep2_A2D||dep1_M2D||dep2_M2D;

 wire stall_F=stall_D;
 
  // Get register values, immediate operands, and pcplus to next stage
  reg [(DBITS-1):0] regval1_A,regval2_A,pc_A,pcplus_A,pcpred_A;
  reg [(IMMBITS-1):0] rawimm_A;
  always @(posedge clk)
    if(!stall_A)
  {regval1_A,regval2_A,rawimm_A,pc_A,pcplus_A,pcpred_A}<=
  {regval1_D,regval2_D,rawimm_D,pc_D,pcplus_D,pcpred_D};
 
 // These have almost no delay from rawimm (just more wires)
 wire [(DBITS-1):0] immval_A={{(DBITS-IMMBITS){rawimm_A[IMMBITS-1]}},rawimm_A};
 wire [(DBITS-1):0] broffs_A={{(DBITS-IMMBITS-2){rawimm_A[IMMBITS-1]}},rawimm_A,2'b00};
 
 // Determine ALU inputs
 reg aluimm_D; // From decoding logic
 // Get aluimm control signal to A stage
 reg aluimm_A;
 always @(posedge clk)
  if(!stall_A)
   aluimm_A<=aluimm_D;
 wire signed [(DBITS-1):0] aluin1_A=regval1_A;
 wire signed [(DBITS-1):0] aluin2_A=aluimm_A?immval_A:regval2_A;
 // Create the actual ALU
 reg [5:0] alufunc_D,alufunc_A;
 // Get ALU control signals to A stage
 always @(posedge clk)
  if(!stall_A) begin
   {alufunc_A}<=
   {alufunc_D};
  end
  wire signed [(DBITS-1):0] aluout_A;
  /*always @(alufunc_A or aluin1_A or aluin2_A)
    case(alufunc_A)
  OP2_AND:  aluout_A=aluin1_A&aluin2_A;
  OP2_OR:   aluout_A=aluin1_A|aluin2_A;
  OP2_XOR:  aluout_A=aluin1_A^aluin2_A;
  OP2_NAND: aluout_A=~(aluin1_A&aluin2_A);
  OP2_NOR:  aluout_A=~(aluin1_A|aluin2_A);
  OP2_NXOR: aluout_A=~(aluin1_A^aluin2_A);
  OP2_ADD:  aluout_A=aluin1_A+aluin2_A;
  OP2_SUB:  aluout_A=aluin1_A-aluin2_A;
  OP2_EQ:   aluout_A=(aluin1_A==aluin2_A);
  OP2_LT:   aluout_A=(aluin1_A<aluin2_A);
  OP2_LE:   aluout_A=(aluin1_A<=aluin2_A);
  OP2_NE:   aluout_A=(aluin1_A!=aluin2_A);
  OP2_GE:   aluout_A=(aluin1_A>=aluin2_A);
  OP2_GT:   aluout_A=(aluin1_A>aluin2_A);
  OP2_LOHI: aluout_A={aluin2_A[31:16],aluin1_A[15:0]};
    default:  aluout_A={DBITS{1'bX}};
  endcase*/
  ALU(aluin1_A, aluin2_A, alufunc_A, alufunc_A != OP2_ADD, aluout_A);
 // Determine PC for the next instruction to fetch
 // This is the value of "target PC for conditional branches"
 wire [(DBITS-1):0] brtarg_D=pcplus_D+broffs_D;
 reg  [(DBITS-1):0] brtarg_A;
 always @(posedge clk)
  if(!stall_A)
   brtarg_A<=brtarg_D;
 // jmptarg_? is the value of "target PC for jumps"
 wire [(DBITS-1):0] jmptarg_A=regval1_A+broffs_A;
 // This is how we decide the target address
 reg isbranch_D,isjump_D; // From decoding logic
 // Get these signals to stage 2
 reg isbranch_A,isjump_A;
 always @(posedge clk)
  if(!stall_A)
   {isbranch_A,isjump_A}<={isbranch_D,isjump_D};
 // Branch condition and next-PC selection
 wire brcond_A=aluout_A[0];
 wire dobranch_A=isbranch_A&&brcond_A;
 wire [(DBITS-1):0] pcgood_A=
  dobranch_A?brtarg_A:
  isjump_A?jmptarg_A:
  pcplus_A;
 wire mispred_A=(pcgood_A!=pcpred_A)&&!isnop_A;
 wire mispred_B=mispred_A;
 wire flush_D=mispred_B;

 wire [(DBITS-1):0] pcgood_B=pcgood_A;
 wire flush_A=1'b0;
 
 
 reg [(DBITS-1):0] aluout_M,pc_M,pcplus_M,pcgood_M;
 always @(posedge clk)
  {aluout_M,pc_M,pcplus_M,pcgood_M}<=
  {aluout_A,pc_A,pcplus_A,pcgood_A};

 wire [(DBITS-1):0] memaddr_M=aluout_M;

 // Value written to memory comes from second register operand
 wire [(DBITS-1):0] wmemval_A=regval2_A;
 // Get it to M stage
 reg [(DBITS-1):0] wmemval_M;
 always @(posedge clk)
  wmemval_M<=wmemval_A;

   // Must-get-right signals - wrmem, wrreg, isnop
 reg wrmem_D, wrreg_D, isnop_D; // From decoding logic
 // Get them to A stage
 reg wrmem_A, wrreg_A, isnop_A;
 always @(posedge clk or posedge reset)
  if(reset)
   {wrmem_A,wrreg_A,isnop_A}<=3'b001;
  else if(flush_D||(stall_D&&!stall_A))
   {wrmem_A,wrreg_A,isnop_A}<=3'b001;
  else if(!stall_A)
   {wrmem_A,wrreg_A,isnop_A}<={wrmem_D,wrreg_D,isnop_D};
 // Get them to M stage
 reg wrmem_M,wrreg_M,isnop_M,mispred_M;
 always @(posedge clk or posedge reset)
  if(reset)
   {wrmem_M,wrreg_M,isnop_M}<=3'b001;
  else if(flush_A)
   {wrmem_M,wrreg_M,isnop_M}<=3'b001;
  else
   {wrmem_M,wrreg_M,isnop_M}<=
   {wrmem_A,wrreg_A,isnop_A};
 // Now the real data memory
 wire MemEnable=!(memaddr_M[(DBITS-1):DMEMADDRBITS]);
 wire MemWE=lock&wrmem_M&MemEnable;
 (* /*ram_init_file = DMEMINITFILE,*/ ramstyle="no_rw_check" *)
 reg [(DBITS-1):0] dmem[(DMEMWORDS-1):0];
 always @(posedge clk)
  if(MemWE)
   dmem[memaddr_M[(DMEMADDRBITS-1):DMEMWORDBITS]]<=wmemval_M;
 wire [(DBITS-1):0] MemVal=MemWE?{DBITS{1'bX}}:dmem[memaddr_M[(DMEMADDRBITS-1):DMEMWORDBITS]];
 // Connect memory and input devices to the bus
 wire [(DBITS-1):0] memout_M=
  MemEnable?MemVal:dbus;
  
  wire [31:0] abus;
  tri  [31:0] dbus;
  wire we;
  
  assign abus = memaddr_M;
  assign we = wrmem_M;
  assign dbus = wrmem_M ? wmemval_M : {32{1'bZ}};
  
  // HEX display
	reg [15:0] HexOut;
	wire [15:0] HexModuleOut;
	wire blink;
	always @(posedge clk)
		HexOut <= HexModuleOut;
	HexDisp #(.BITS(16), .ADDRDATA(ADDRHEX), .ADDRBLNK(ADDRHBLNK), .CLOCKS_PER_MS(CLOCKS_PER_MS)) HexDisp1(clk, lock, abus, dbus, we, HexModuleOut, blink);
	SevenSeg ss3(HEX3, HexOut[15:12], blink);
	SevenSeg ss2(HEX2, HexOut[11:8], blink);
	SevenSeg ss1(HEX1, HexOut[7:4], blink);
	SevenSeg ss0(HEX0, HexOut[3:0], blink);
			
	// LEDG and LEDR devices
	LED #(.BITS(10), .BASE(ADDRLEDR)) LED_red(clk, lock, abus, dbus, we, LEDRout);
	wire [9:0] LEDRout;
	assign LEDR = LEDRout;
	LED #(.BITS(8), .BASE(ADDRLEDG)) LED_green(clk, lock, abus, dbus, we, LEDGout);
	wire [7:0] LEDGout;
	assign LEDG = LEDGout;
	
	// Timer
	Timer #(.CLOCKS_PER_MS(CLOCKS_PER_MS)) T(clk, lock, abus, dbus, we);
	// Switches
	Switches #(.NUM(10), .ADDRCTRL(ADDRSCTRL), .ADDRDATA(ADDRSDATA), .CLOCKS_IN_10MS(CLOCKS_PER_10MS)) switches(clk, lock, abus, dbus, we, SW);
	// Keys
	Keys #(.NUM(4), .ADDRCTRL(ADDRKCTRL), .ADDRDATA(ADDRKDATA)) keys(clk, lock, abus, dbus, we, KEY);

 // Decide what gets written into the destination register
 reg selaluout_D,selmemout_D,selpcplus_D; // From decoding logic
 // Get result-selection signals to A and then M stage
 reg selaluout_A,selmemout_A,selpcplus_A;
 reg selaluout_M,selmemout_M,selpcplus_M;
 always @(posedge clk) begin
  if(!stall_A)
   {selaluout_A,selmemout_A,selpcplus_A}<={selaluout_D,selmemout_D,selpcplus_D};
  {selaluout_M,selmemout_M,selpcplus_M}<={selaluout_A,selmemout_A,selpcplus_A};
 end
 
 wire [(DBITS-1):0] wregval_M=
  selaluout_M?aluout_M:
  selmemout_M?memout_M:
  selpcplus_M?pcplus_M:
  32'hBEEFDEAD;
 // Register write regno decided by decoding logic
 reg [(REGNOBITS-1):0] wregno_D;
 // Get it to A and then M stage
 reg [(REGNOBITS-1):0] wregno_A, wregno_M;
 always @(posedge clk) begin
  if(!stall_A)
   wregno_A<=wregno_D;
  wregno_M<=wregno_A;
 end

 always @(posedge clk)
  if(wrreg_M&&lock)
   regs[wregno_M]<=wregval_M;

  // Decoding logic
  always @(inst_D or op1_D or op2_D or ry_D or rz_D or flush_D) begin
    {aluimm_D,alufunc_D}=
  {    1'bX,    6'hXX};
  {isbranch_D,isjump_D,isnop_D,wrmem_D}=
  {      1'b0,    1'b0,   1'b0,   1'b0};
    {selaluout_D,selmemout_D,selpcplus_D,wregno_D,wrreg_D}=
  {       1'bX,       1'bX,       1'bX,   5'hXX,   1'b0};
  case(op1_D)
    OP1_ALUR:
   if(inst_D==32'b0)
    isnop_D=1'b1;
   else begin
    {aluimm_D,alufunc_D,selaluout_D,selmemout_D,selpcplus_D,wregno_D,wrreg_D}=
    {    1'b0, op2_D   ,       1'b1,       1'b0,       1'b0,    rz_D,   1'b1};
   end
  OP1_ANDI: begin
    {aluimm_D,alufunc_D,selaluout_D,selmemout_D,selpcplus_D,wregno_D,wrreg_D}=
    {    1'b1, OP2_AND ,       1'b1,       1'b0,       1'b0,    ry_D,   1'b1};
   end
  OP1_ORI:  begin
    {aluimm_D,alufunc_D,selaluout_D,selmemout_D,selpcplus_D,wregno_D,wrreg_D}=
    {    1'b1, OP2_OR  ,       1'b1,       1'b0,       1'b0,    ry_D,   1'b1};
   end
  OP1_ADDI: begin
    {aluimm_D,alufunc_D,selaluout_D,selmemout_D,selpcplus_D,wregno_D,wrreg_D}=
            {    1'b1,  OP2_ADD,       1'b1,       1'b0,       1'b0,    ry_D,   1'b1};
   end
  OP1_HI: begin
    {aluimm_D,alufunc_D,selaluout_D,selmemout_D,selpcplus_D,wregno_D,wrreg_D}=
            {    1'b1, OP2_LOHI,       1'b1,       1'b0,       1'b0,    ry_D,   1'b1};
   end
    OP1_LW:   {aluimm_D,alufunc_D,selaluout_D,selmemout_D,selpcplus_D,wregno_D,wrreg_D}=
                {    1'b1,  OP2_ADD,       1'b0,       1'b1,       1'b0,    ry_D,   1'b1};
    OP1_SW:   {aluimm_D,alufunc_D,wrmem_D}=
                {    1'b1,  OP2_ADD,   1'b1};
  OP1_JAL:  {isjump_D,selaluout_D,selmemout_D,selpcplus_D,wregno_D,wrreg_D}=
      {    1'b1,       1'b0,       1'b0,       1'b1,    ry_D,   1'b1};
  OP1_BEQ:  {aluimm_D,alufunc_D,isbranch_D}=
                {    1'b0,   OP2_EQ,      1'b1};
  OP1_BNE:  {aluimm_D,alufunc_D,isbranch_D}=
                {    1'b0,   OP2_NE,      1'b1};
  default:  ;
  endcase
  end
endmodule
